﻿using System.Collections.Generic;
using System.Diagnostics;
using GemTowerDefence.Towers;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace GemTowerDefence
{
  public class Player
  {
    private readonly Dictionary<TowerType, Texture2D> _towersTexture;
    private readonly Texture2D _missileTexture;
    private readonly List<Tower> _towers;

    private readonly TileMap _tileMap;

    private MouseState _mouseState; // Mouse state for the current frame
    private MouseState _oldState; // Mouse state for the previous frame

    private Tower _selectedTower;

    public int Money { get; internal set; }

    public int Lives { get; internal set; }

    public TowerType NewTowerType { get; set; }

    public int AvailableGems { get; set; }

    public TileMap Map
    {
      get { return _tileMap; }
    }

    public int InitialGems = 5;
    private KeyboardState _prevKeyState;

    public Player(TileMap tileMap, Dictionary<TowerType, Texture2D> towersTexture, Texture2D missileTexture)
    {
      AllowAddTower = true;
      Money = 0;
      Lives = 30;
      AvailableGems = InitialGems;
      _towers = new List<Tower>();
      _tileMap = tileMap;
      _towersTexture = towersTexture;
      _missileTexture = missileTexture;
    }

    public bool IsAddingTower { get; set; }

    public Tower AddingTower { get; private set; }

    public bool AllowAddTower { get; set; }

    public void Update(GameTime gameTime, List<Enemy> enemies)
    {
      var keyState = Keyboard.GetState();
      if (keyState.IsKeyUp(Keys.R) && _prevKeyState.IsKeyDown(Keys.R) && AvailableGems > 0 && !IsAddingTower)
        NewTowerType = TowerType.Rock;
      _prevKeyState = keyState;

      _mouseState = Mouse.GetState();

      if (IsAddingTower && !AllowAddTower)
      {
        _towers.Remove(AddingTower);
        IsAddingTower = false;
        AllowAddTower = true;
        AvailableGems++;
      }

      var cellX = _mouseState.X / Game.TextureSize; // Convert the position of the mouse
      var cellY = _mouseState.Y / Game.TextureSize; // from array space to level space

      if (_mouseState.LeftButton == ButtonState.Released && _oldState.LeftButton == ButtonState.Pressed)
      {
        if (IsCellClear(cellX, cellY) && !IsAddingTower && NewTowerType != TowerType.None)
        {
          IsAddingTower = true;
          AddingTower = AddTower(cellX, cellY);
        }
        else
        {
          if (_selectedTower != null)
          {
            if (!_selectedTower.Bounds.Contains(_mouseState.X, _mouseState.Y))
            {
              _selectedTower.IsSelected = false;
              _selectedTower = null;
            }
          }
          foreach (var tower in _towers)
          {
            if (tower == _selectedTower)
              continue;

            if (tower.Bounds.Contains(_mouseState.X, _mouseState.Y))
            {
              _selectedTower = tower;
              tower.IsSelected = true;
            }
          }
        }
      }

      foreach (var tower in _towers)
      {
        if (!tower.HasTarget)
          tower.GetClosestEnemy(enemies);
        tower.Update(gameTime);
      }

      _oldState = _mouseState; // Set the oldState so it becomes the state of the previous frame.
    }

    /// <summary>
    /// Adds a tower to the player's collection.
    /// </summary>
    public Tower AddTower(int cellX, int cellY)
    {
      Debug.Assert(AvailableGems != 0);

      IsAddingTower = true;

      var tileX = cellX * Game.TextureSize;
      var tileY = cellY * Game.TextureSize;

      Tower towerToAdd = null;
      switch (NewTowerType)
      {
        case TowerType.Rock:
          towerToAdd = new Rock(_towersTexture[NewTowerType], _missileTexture, new Vector2(tileX, tileY));
          break;
        case TowerType.Amethyst:
          towerToAdd = new Amethyst(_towersTexture[NewTowerType], _missileTexture, new Vector2(tileX, tileY));
          break;
        case TowerType.Aquamarine:
          towerToAdd = new Aquamarine(_towersTexture[NewTowerType], _missileTexture, new Vector2(tileX, tileY));
          break;
        case TowerType.Emerald:
          towerToAdd = new Emerald(_towersTexture[NewTowerType], _missileTexture, new Vector2(tileX, tileY));
          break;
      }

      Debug.Assert(towerToAdd != null);
      // Only add the tower if there is a space and if the player can afford it.
      if (IsCellClear(cellX, cellY))
      {
        _towers.Add(towerToAdd);
        AvailableGems--;

        // Reset the newTowerType field.
        NewTowerType = TowerType.None;
      }
      return towerToAdd;
    }

    private bool IsCellClear(int cellX, int cellY)
    {
      var inBounds = cellX >= 0 && cellY >= 0 && // Make sure tower is within limits
          cellX < _tileMap.Width && cellY < _tileMap.Height;

      var spaceClear = true;

      foreach (var tower in _towers) // Check that there is no tower here
      {
        var tileX = cellX * Game.TextureSize;
        var tileY = cellY * Game.TextureSize;
        spaceClear = (tower.Position != new Vector2(tileX, tileY));
        if (!spaceClear)
          break;
      }

      var cellType = _tileMap.GetCellType(cellX, cellY);
      var onPath = (cellType == TileType.Plate);

      return inBounds && spaceClear && !onPath; // If both checks are true return true
    }

    public bool IsCellNavigatabe(int cellX, int cellY)
    {
      var inBounds = cellX >= 0 && cellY >= 0 && // Make sure tower is within limits
          cellX < _tileMap.Width && cellY < _tileMap.Height;

      var spaceClear = true;

      foreach (var tower in _towers) // Check that there is no tower here
      {
        var tileX = cellX * Game.TextureSize;
        var tileY = cellY * Game.TextureSize;
        spaceClear = (tower.Position != new Vector2(tileX, tileY));
        if (!spaceClear)
          break;
      }
      return inBounds && spaceClear;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
      foreach (var tower in _towers)
        tower.Draw(spriteBatch);
    }

    public void DrawPreview(SpriteBatch spriteBatch)
    {
      // Draw the tower preview.
      if (NewTowerType == TowerType.None) return;

      var cellX = _mouseState.X / Game.TextureSize; // Convert the position of the mouse
      var cellY = _mouseState.Y / Game.TextureSize; // from array space to level space

      var tileX = cellX * Game.TextureSize; // Convert from array space to level space
      var tileY = cellY * Game.TextureSize; // Convert from array space to level space

      var previewTexture = _towersTexture[NewTowerType];

      var destRect = new Rectangle(tileX, tileY, previewTexture.Width, previewTexture.Height);
      spriteBatch.Draw(previewTexture, destRect, Color.White);
    }
  }
}
